package com.kh.tool.mapvalidator;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.kh.tool.mapvalidator.exception.MissingParameterException;
import net.sf.json.JSONArray;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.*;
import java.util.stream.Collectors;

public abstract class KhValidationUtils {

    /***
     * 先将Controller中的Map请求参数转换成相对应的实体后，以纯正的java实体加注解的方式校验请求入参
     * @param req
     * @param clazz
     * @param <T>
     */
    public static  <T> void checkByAnnotationWay(Map<String,Object> req, Class<T> clazz) {
        ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
        Validator validator = vf.getValidator();
        T body = mapToBean(req, clazz);
        Set<ConstraintViolation<T>> validate = validator.validate(body);
        if (CollectionUtils.isNotEmpty(validate)) {
            List<Object> errorList = validate.stream().map(o -> o.getMessage()).collect(Collectors.toList());
            throw new MissingParameterException(errorList);
        }
    }

    /***
     * 直接用暴力方式对Controller中的Map参数进行校验（针对于不想定义JavaBean的情况下使用这种方式 如果Map有对应的JavaBean则使用checkReqParamByEntityAnnotation方法校验）
     * @param target
     * @param validator
     */
    public static void checkByBadWay(Map<String,Object> target, org.springframework.validation.Validator validator) {
        if (MapUtils.isEmpty(target)) {
            throw new MissingParameterException(Arrays.asList("参数缺失"));
        }
        CustomMapBindingResult errors = new CustomMapBindingResult("",target);
        validator.validate(target,errors);
        if (errors.hasErrors()) {
            Set<Object> errorSet = errors.getFieldErrors().stream().map(o -> o.getField() + " " + o.getCode()).collect(Collectors.toSet());
            throw new MissingParameterException(errorSet);
        }
    }

    private static <T> T mapToBean(Map<String, Object> map, Class<T> beanClass) {
        return JSONObject.parseObject(JSON.toJSONString(map),beanClass);
    }

    public static void rejectListItemIfEmpty(Errors errors,String field, ParamValueValidator... valueValidator) {
        Node head = buildNode(field);
        final CustomMapBindingResult bindingResult = (CustomMapBindingResult) errors;
        check(head, "", errors, bindingResult,valueValidator);
    }

    public static void rejectIfEmpty(Errors errors, String field, ParamValueValidator... valueValidator) {
        final CustomMapBindingResult bindingResult = (CustomMapBindingResult) errors;
        Object value = bindingResult.getActualFieldValue(field);
        if (value == null || !StringUtils.hasLength(value.toString())) {
            errors.rejectValue(field,"不能为空");
        } else {
            if (!ArrayUtils.isEmpty(valueValidator) && !valueValidator[0].validator(value)) {
                errors.rejectValue(field,"参数不合法 入参值:" + value);
            }
        }
    }

    private static void check(Node head,String prefixField,Errors errors, CustomMapBindingResult bindingResult, ParamValueValidator... valueValidator) {
        String field = prefixField + head.key;
        if (head.next == null) {
            rejectIfEmpty(errors,field,valueValidator);
            return;
        }
        Object actualFieldValue = bindingResult.getActualFieldValue(field);
        if (actualFieldValue == null) {
            errors.rejectValue(field,"不能为空");
            return;
        }
        if (actualFieldValue instanceof JSONArray) {
            JSONArray jsonArray = (JSONArray) actualFieldValue;
            if (CollectionUtils.isEmpty(jsonArray)) {
                errors.rejectValue(field,"不能为空");
            } else {
                for (int i = 0; i < jsonArray.size(); i++) {
                    check(head.next, field + "(" + i + ").", errors, bindingResult,valueValidator);
                }
            }

        } else if (actualFieldValue instanceof net.sf.json.JSONObject) {
            net.sf.json.JSONObject jsonObject = (net.sf.json.JSONObject) actualFieldValue;
            if (jsonObject.isEmpty()) {
                errors.rejectValue(field,"不能为空");
            } else {
                check(head.next,field+".",errors,bindingResult,valueValidator);
            }
        }
    }

    /***
     * 将field表达式转换成内部简单链表结构 方便递归处理
     * @param field
     * @return
     */
    private static Node buildNode(String field) {
        String[] arr = field.split("\\.");
        Node next = null;
        Node head = null;
        for (int i = arr.length - 1; i >= 0; i--) {
            Node node = new Node(arr[i],next);
            head = node;
            next = node;
        }
        return head;
    }

    /**
     * 自定义简单链表结构
     */
    private static class Node {
        public String key;
        public Node next;

        public Node(String key, Node next) {
            this.key = key;
            this.next = next;
        }
    }


}
